/*******************************************************************************
 * Copyright (c) 2009 Ravenhearte Design.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 	*	Redistributions of source code must retain the above copyright
 * 		notice, this list of conditions and the following disclaimer.
 * 
 * 	*	Redistributions in binary form must reproduce the above copyright
 * 		notice, this list of conditions and the following disclaimer in the
 * 		documentation and/or other materials provided with the distribution.
 * 
 * 	*	Neither the name of 'Ravenhearte Design' nor the names of its contributors 
 * 		may be used to endorse or promote products derived from this software 
 * 		without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/

package org.ravenhearte.engine.input;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

import org.ravenhearte.engine.input.InputEvent.EventType;

import com.jme.input.KeyInput;
import com.jme.input.KeyInputListener;
import com.jme.input.MouseInput;
import com.jme.input.MouseInputListener;

public class InputEventManager implements KeyInputListener, MouseInputListener {
	public static InputEventManager eM;
	private HashMap<InputEvent.EventType, LinkedList<InputEvent>> gui, world;

	public enum Queue {
		GUI, WORLD
	}

	public InputEventManager() {
		eM = this;
		gui = new HashMap<InputEvent.EventType, LinkedList<InputEvent>>();
		world = new HashMap<InputEvent.EventType, LinkedList<InputEvent>>();
		MouseInput.get().addListener(this);
		KeyInput.get().addListener(this);
	}

	public void registerEvent(Queue queue, InputEvent evnt) {
		switch (queue) {
		case GUI:
			if (gui.containsKey(evnt.type)) {
				gui.get(evnt.type).add(evnt);
			} else {
				gui.put(evnt.type, new LinkedList<InputEvent>());
				gui.get(evnt.type).add(evnt);
			}
			break;
		case WORLD:
			if (world.containsKey(evnt.type)) {
				world.get(evnt.type).add(evnt);
			} else {
				world.put(evnt.type, new LinkedList<InputEvent>());
				world.get(evnt.type).add(evnt);
			}
			break;
		}
	}

	public void unregisterEvents(EventHandler eH) {
		Iterator<LinkedList<InputEvent>> it = gui.values().iterator();
		while(it.hasNext()) {
			Iterator<InputEvent> i2 = it.next().iterator();
			while(i2.hasNext()) {
				InputEvent ev = i2.next();
				if(ev.parent == eH) {
					i2.remove();
				}
			}
		}
		
		it = world.values().iterator();
		while(it.hasNext()) {
			Iterator<InputEvent> i2 = it.next().iterator();
			while(i2.hasNext()) {
				InputEvent ev = i2.next();
				if(ev.parent == eH) {
					i2.remove();
				}
			}
		}
	}
	
	public void unregisterEvent(EventHandler eH, EventType type, int id) {
		Iterator<LinkedList<InputEvent>> it = gui.values().iterator();
		while(it.hasNext()) {
			Iterator<InputEvent> i2 = it.next().iterator();
			while(i2.hasNext()) {
				InputEvent ev = i2.next();
				if(ev.parent == eH && ev.type == type && ev.id == id) {
					i2.remove();
				}
			}
		}
		
		it = world.values().iterator();
		while(it.hasNext()) {
			Iterator<InputEvent> i2 = it.next().iterator();
			while(i2.hasNext()) {
				InputEvent ev = i2.next();
				if(ev.parent == eH && ev.type == type && ev.id == id) {
					i2.remove();
				}
			}
		}
	}

	@Override
	public void onKey(char character, int keyCode, boolean pressed) {
		boolean processed = false;
		if (pressed && gui.containsKey(EventType.KEYDOWN)) {
			Iterator<InputEvent> evnts = gui.get(EventType.KEYDOWN).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == keyCode) {
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
		if (pressed && !processed && world.containsKey(EventType.KEYDOWN)) {
			Iterator<InputEvent> evnts = world.get(EventType.KEYDOWN)
					.iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == keyCode) {
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
		if (!pressed && gui.containsKey(EventType.KEYUP)) {
			Iterator<InputEvent> evnts = gui.get(EventType.KEYUP).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == keyCode) {
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
		if (!pressed && !processed && world.containsKey(EventType.KEYUP)) {
			Iterator<InputEvent> evnts = world.get(EventType.KEYUP).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == keyCode) {
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
	}

	@Override
	public void onButton(int button, boolean pressed, int x, int y) {
		boolean processed = false;
		if (pressed && gui.containsKey(EventType.BUTTONDOWN)) {
			Iterator<InputEvent> evnts = gui.get(EventType.BUTTONDOWN)
					.iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == button) {
					evnt.x = x;
					evnt.y = y;
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
		if (pressed && !processed && world.containsKey(EventType.BUTTONDOWN)) {
			Iterator<InputEvent> evnts = world.get(EventType.BUTTONDOWN)
					.iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == button) {
					evnt.x = x;
					evnt.y = y;
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
		if (!pressed && gui.containsKey(EventType.BUTTONUP)) {
			Iterator<InputEvent> evnts = gui.get(EventType.BUTTONUP).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == button) {
					evnt.x = x;
					evnt.y = y;
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
		if (!pressed && !processed && world.containsKey(EventType.BUTTONUP)) {
			Iterator<InputEvent> evnts = world.get(EventType.BUTTONUP)
					.iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				if (evnt.id == button) {
					evnt.x = x;
					evnt.y = y;
					processed = evnt.parent.onEvent(evnt);
				}
			}
		}
	}

	@Override
	public void onMove(int delta, int delta2, int newX, int newY) {
		boolean processed = false;
		if (gui.containsKey(EventType.MOVE)) {
			Iterator<InputEvent> evnts = gui.get(EventType.MOVE).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				evnt.delta = delta;
				evnt.delta2 = delta2;
				evnt.x = newX;
				evnt.y = newY;
				processed = evnt.parent.onEvent(evnt);
			}
		}
		if (!processed && world.containsKey(EventType.MOVE)) {
			Iterator<InputEvent> evnts = world.get(EventType.MOVE).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				evnt.delta = delta;
				evnt.delta2 = delta2;
				evnt.x = newX;
				evnt.y = newY;
				processed = evnt.parent.onEvent(evnt);
			}
		}
	}

	@Override
	public void onWheel(int wheelDelta, int x, int y) {
		boolean processed = false;
		if (gui.containsKey(EventType.WHEEL)) {
			Iterator<InputEvent> evnts = gui.get(EventType.WHEEL).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				evnt.delta = wheelDelta;
				evnt.x = x;
				evnt.y = y;
				processed = evnt.parent.onEvent(evnt);
			}
		}
		if (!processed && world.containsKey(EventType.WHEEL)) {
			Iterator<InputEvent> evnts = world.get(EventType.WHEEL).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				evnt.delta = wheelDelta;
				evnt.x = x;
				evnt.y = y;
				processed = evnt.parent.onEvent(evnt);
			}
		}
	}

	public void generateEvent(InputEvent ev) {
		boolean processed = false;
		if (gui.containsKey(ev.type)) {
			Iterator<InputEvent> evnts = gui.get(ev.type).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				processed = evnt.parent.onEvent(ev);
			}
		}
		if (!processed && world.containsKey(ev.type)) {
			Iterator<InputEvent> evnts = world.get(ev.type).iterator();
			while (evnts.hasNext() && !processed) {
				InputEvent evnt = evnts.next();
				processed = evnt.parent.onEvent(ev);
			}
		}
	}
}
